use chrono::Local;
use serde::Deserialize;
use serde::Serialize;
use wechat_pay_rust_sdk::model::{AmountInfo, MicroParams, PayerInfo};
use wechat_pay_rust_sdk::pay::WechatPay;
use wechat_pay_rust_sdk::response::MicroResponse;

use crate::config::app_config::CONFIG;
use crate::service::DATE_FORMAT;
use crate::SQLX;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WechatOrder {
    wechat_order_id: Option<i64>,
    open_id: Option<String>,
    out_trade_no: Option<String>,
    transaction_id: Option<String>,
    prepay_id: Option<String>,
    amount: Option<i32>,
    status: Option<u8>,
    create_time: Option<String>,
}

impl WechatOrder {
    async fn insert_order(&self) -> anyhow::Result<()> {
        let db = SQLX.get().ok_or(anyhow::anyhow!("sqlx error"))?;
        let result = sqlx::query(r#"insert into wechat_order(open_id,out_trade_no,transaction_id,prepay_id,amount,status,create_time)values(?,?,?,?,?,?)"#)
            .bind(&self.open_id).bind(&self.out_trade_no).bind(&self.prepay_id).bind(self.amount).bind(OrderStatus::get_value(OrderStatus::NOPAY)).bind(Local::now().format(DATE_FORMAT).to_string()).execute(db).await?;
        log::info!("create order result = {:?}", result);
        Ok(())
    }
}

fn get_wechat_pay() -> anyhow::Result<WechatPay> {
    let app_id = &CONFIG.wechat_config.app_id;
    let mac_id = &CONFIG.wechat_config.mch_id;
    let private_key = &CONFIG.wechat_config.private_key;
    let serial_no = &CONFIG.wechat_config.serial_no;
    let v3_key = &CONFIG.wechat_config.v3_key;
    let notify_url = &CONFIG.wechat_config.notify_url;
    let wechat_pay = WechatPay::new(app_id, mac_id, private_key, serial_no, v3_key, notify_url);
    Ok(wechat_pay)
}

pub async fn create_order(
    description: &str,
    amount: i32,
    open_id: &str,
) -> anyhow::Result<MicroResponse> {
    let out_trade_no = Local::now().to_string();
    let response = get_wechat_pay()?
        .micro_pay(MicroParams::new(
            description,
            &out_trade_no,
            AmountInfo::from(amount),
            PayerInfo::from(open_id),
        ))
        .await?;
    // insert order
    Ok(response)
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OrderStatus {
    //已下单未支付
    NOPAY = 1,
    //下单并支付
    PAYED = 2,
    //下单并支付，但未完成订单
    UNFINISHED = 3,
    //下单并支付，并完成订单
    FINISHED = 4,
    //订单完成并退款
    REFUND = 5,
}
impl OrderStatus {
    pub fn get_value(order_status: OrderStatus) -> i32 {
        match order_status {
            OrderStatus::NOPAY => 1,
            OrderStatus::PAYED => 2,
            OrderStatus::UNFINISHED => 3,
            OrderStatus::FINISHED => 4,
            OrderStatus::REFUND => 5,
        }
    }
}
